package base

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"reflect"
	"xorm.io/xorm/names"

	//_ "modernc.org/sqlite"
	"xorm.io/xorm"
)

type engine struct {
	*xorm.Engine
	*log.Logger
}

func (e *engine) Log(query string, args []interface{}) {
	e.Engine.Logger().ShowSQL()
	e.Logger.Println(query, args)
}

var DB = func() *xorm.Engine {
	//x := Must2(xorm.NewEngine("sqlite", WorkDir+"data.db"))
	//x := Must2(xorm.NewEngine("mysql", "root:LsqWSxQt@tcp("+Config.DBHost+")/knot?charset=utf8mb4"))
	x := Must2(xorm.NewEngine("mysql", Config.DBUser+":"+Config.DBPassword+"@tcp("+Config.DBHost+")/"+Config.DBName+"?charset=utf8mb4"))
	if err := x.Ping(); err != nil {
		log.Fatalln("连接数据库失败", err)
	}
	Cover(x.Ping(), "")
	x.SetTableMapper(names.NewPrefixMapper(names.SnakeMapper{}, "t_"))
	x.ShowSQL(true)
	return x
}()

func Random(s *xorm.Session) string {
	v := Must2(s.Engine().DBVersion())
	if v.Edition == "sqlite" {
		return "RANDOM()"
	} else {
		return "rand()"
	}
}

type op struct {
	db xorm.Interface
}

func (op *op) target(v any) xorm.Interface {
	if _v, ok := v.(interface {
		Query(DB xorm.Interface) *xorm.Session
	}); ok {
		return _v.Query(op.db)
	}
	typ := reflect.TypeOf(v)
	switch typ.Kind() {
	case reflect.Array, reflect.Slice:
		return op.target(reflect.New(typ.Elem()).Elem().Interface())
	case reflect.Ptr:
		return op.target(reflect.ValueOf(v).Elem().Interface())
	}
	return op.db
}

func (op *op) Where(query interface{}, args ...interface{}) *op {
	op.db = op.db.Where(query, args...)
	return op
}

func (op *op) Or(query interface{}, args ...interface{}) *op {
	s, ok := op.db.(*xorm.Session)
	if !ok {
		panic(fmt.Sprintf("无法在这里使用Or语句"))
	}
	op.db = s.Or(query, args...)
	return op
}

func (op *op) Find(beans interface{}, condiBeans ...interface{}) error {
	return op.target(beans).Find(beans, condiBeans...)
}

func (op *op) FindAndCount(rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) {
	return op.target(rowsSlicePtr).FindAndCount(rowsSlicePtr, condiBean...)
}

func (op *op) OrderBy(order interface{}, args ...interface{}) *op {
	op.db = op.db.OrderBy(order, args...)
	return op
}

func (op *op) Desc(colNames ...string) *op {
	op.db = op.db.Desc(colNames...)
	return op
}

func (op *op) Limit(limit int, start ...int) *op {
	op.db = op.db.Limit(limit, start...)
	return op
}

func (op *op) Join(joinOperator string, tablename interface{}, condition interface{}, args ...interface{}) *op {
	op.db = op.db.Join(joinOperator, tablename, condition, args...)
	return op
}

func (op *op) Select(str string) *op {
	op.db = op.db.Select(str)
	return op
}

func Query(x xorm.Interface) *op {
	return &op{db: x}
}
